﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System.Collections.Generic;


namespace imvs
{
    public class DictUtils
    {
        /// <summary>
        /// 创建或获取命名字典
        /// </summary>
        public static ObjectId CreateDict(string dictName, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            DBDictionary namedDict = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);

            ObjectId objdId = ObjectId.Null;
            if (namedDict.Contains(dictName))
            {
                objdId = namedDict.GetAt(dictName);
            }
            else
            {
                DBDictionary newDict = new DBDictionary();
                namedDict.UpgradeOpen();
                namedDict.SetAt(dictName, newDict);
                tr.AddNewlyCreatedDBObject(newDict, true);
                objdId = newDict.ObjectId;
                if (needCommit) { tr.Commit(); tr.Dispose(); }
            }

            return objdId;
        }

        /// <summary>
        /// 删除整个字典
        /// </summary>
        public static bool RemoveDict(string dictName, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            DBDictionary namedDict = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForWrite);

            bool ret = false;
            if (namedDict.Contains(dictName))
            {
                namedDict.Remove(dictName);
                ret = true;
            }

            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return ret;
        }

        /// <summary>
        /// 删除字典中的键值对
        /// </summary>
        public static bool RemoveDictData(string dictName, string key, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            ObjectId dictId = GetDictObjectId(dictName, tr);
            if (dictId.IsNull) return false;

            bool ret = false;
            DBDictionary dict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForWrite);
            if (dict.Contains(key))
            {
                dict.Remove(key);
                ret = true;
            }

            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return ret;
        }

        /// <summary>
        /// 设置字典数据（支持多种数据类型）
        /// </summary>
        public static bool SetDictData(string dictName, string key, object value, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            ObjectId dictId = GetDictObjectId(dictName, tr);
            if (dictId.IsNull) return false;

            DBDictionary dict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForWrite);
            Xrecord xrec = new Xrecord();

            ResultBuffer rb = new ResultBuffer();
            rb.Add(new TypedValue(300, key)); // 键名

            if (value is string)
                rb.Add(new TypedValue(1, value));
            else if (value is int)
                rb.Add(new TypedValue(70, value));
            else if (value is double)
                rb.Add(new TypedValue(40, value));
            else
                rb.Add(new TypedValue(1, value.ToString()));

            xrec.Data = rb;

            if (dict.Contains(key))
                dict.Remove(key);

            dict.SetAt(key, xrec);
            tr.AddNewlyCreatedDBObject(xrec, true);

            if (needCommit) { tr.Commit(); tr.Dispose(); }

            return true;
        }

        /// <summary>
        /// 获取字典数据
        /// </summary>
        public static object GetDictData(string dictName, string key, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            object value = null;

            ObjectId dictId = GetDictObjectId(dictName, tr);
            if (dictId.IsValid)
            {
                DBDictionary dict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForRead);
                if (dict.Contains(key))
                {
                    Xrecord xrec = (Xrecord)tr.GetObject(dict.GetAt(key), OpenMode.ForRead);

                    foreach (TypedValue tv in xrec.Data)
                    {
                        if (tv.TypeCode == 1) { value = tv.Value as string; break; }
                        if (tv.TypeCode == 70) { value = (int)tv.Value; break; }
                        if (tv.TypeCode == 40) { value = (double)tv.Value; break; }
                    }
                }
            }

            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return value;
        }

        /// <summary>
        /// 获取字典条目数量
        /// </summary>
        public static int CountDictDatas(string dictName, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }
            int count = 0;
            ObjectId dictId = GetDictObjectId(dictName, tr);
            if (dictId.IsValid)
            {
                DBDictionary dict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForRead);
                count = dict.Count;
            }

            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return count;
        }

        /// <summary>
        /// 列出字典所有键值对
        /// </summary>
        public static Dictionary<string, object> ListDictDatas(string dictName, Transaction tr = null)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            var result = new Dictionary<string, object>();
            ObjectId dictId = GetDictObjectId(dictName, tr);
            if (dictId.IsNull) return result;

            DBDictionary dict = (DBDictionary)tr.GetObject(dictId, OpenMode.ForRead);
            foreach (DBDictionaryEntry entry in dict)
            {
                if (entry.Value.ObjectClass.DxfName == "XRECORD")
                {
                    Xrecord xrec = (Xrecord)tr.GetObject(entry.Value, OpenMode.ForRead);
                    foreach (TypedValue tv in xrec.Data)
                    {
                        if (tv.TypeCode == 300) continue; // 跳过键名
                        result[entry.Key] = tv.Value;
                        break;
                    }
                }
            }
            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return result;
        }

        private static ObjectId GetDictObjectId(string dictName, Transaction tr)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;

            bool needCommit = false;
            if (tr == null)
            {
                tr = db.TransactionManager.StartTransaction();
                needCommit = true;
            }

            DBDictionary namedDict = (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);

            ObjectId objId = namedDict.Contains(dictName) ? namedDict.GetAt(dictName) : ObjectId.Null;
            if (needCommit) { tr.Commit(); tr.Dispose(); }
            return objId;
        }
    }

}
